#include "Board.h"
#include "Utils.h"
#include "Texture.h"
#include "Vector.h"
#include "Cell.h"
#include "time.h"
#include "Diamond.h"
#include "TimeController.h"
#include "math.h"
#include "stdlib.h"
#include "ResMgr.h"
#include "StateMgr.h"
#include "WinState.h"
#include "PlayState.h"
#include "ScoreBoard.h"
#include "GameOverState.h"
#include "GameUtils.h"
#include "GameTimer.h"
#include "AnimationSprite.h"
#include "Animation.h"
#include "Level.h"

EatBlockDiamond::EatBlockDiamond()
{
	diamondBornWhenEatBlockExplode = 0;
}


void EatBlockDiamond::AddDiamond(Diamond* diamond)
{
	list<Diamond*>::iterator i;

	//if exist, return
	for(i = ateList.begin(); i!=ateList.end(); i++)
		if((*i)==diamond)
			return;

	//if not exist, add.
	if(diamond->isAddedToAteList == false)
	{
		ateList.push_back(diamond);
		diamond->isAddedToAteList = true;
		if(diamond->GetEffect() != DME_NORMAL)
		{
			mainDiamond = diamond;
			effect = mainDiamond->GetEffect();
		}
	}
}

void EatBlockDiamond::Pop()
{
	if(ateList.size()!=0)
	{
		ateList.back()->isAddedToAteList = false;
		ateList.pop_back();
	}
}

int EatBlockDiamond::Size()
{
	return ateList.size();
}

bool EatBlockDiamond::IsAllInState(int state)
{
	list<Diamond*>::iterator i;
	for(i = ateList.begin(); i!=ateList.end(); i++)
		if((*i)->GetState()!=state)
			return false;
	return true;
}

void EatBlockDiamond::KillDiamonds()
{
	list<Diamond*>::iterator i;
	if(IsAllInState(DMS_IDLE))
	{
		for(i = ateList.begin(); i!=ateList.end(); i++)
		{
			Diamond* diamond = (*i);	
			diamond->SetIsExploding(true);
		}
	}
}

void EatBlockDiamond::SetDiamondBornWhenEatBlockExplode(Diamond* diamond)
{
	diamondBornWhenEatBlockExplode = diamond;
}

void EatBlockDiamond::SetEffectOfDiamondBornWhenEatBlockExplode(int effect)
{
	effectOfDiamondBornWhenEatBlockExplode = effect;
}

EatBlockDiamond::~EatBlockDiamond()
{
	ateList.clear();
}

Board::Board():MoveableObj()
{
	width = height = cellHeight = cellWidth = 0;
	cells = 0;
	numTypeDiamond = 0;

	dropRowDelay = 0;
	rowDrop = 0;
	activeCell = 0;
	posBackground = new Vector(0, 0);
	touchUpPos = new Vector();
	touchDownPos = new Vector();
}

Board::Board(int x, int y, int cellWidth, int cellHeight)
:MoveableObj(x, y, width*cellWidth, height*cellHeight, 0)
{
	this->cellWidth = cellWidth;
	this->cellHeight = cellHeight;
	cells = 0;
	posBackground = new Vector(0, 0);
	touchUpPos = new Vector();
	touchDownPos = new Vector();
	Reset();
}

void Board::Reset()
{
	numTypeDiamond = NUM_TYPE_DIAMOND_DEFAULT;
	background = 0;
	activeCell = 0;
	updateSomethingDelay = 0;
	checkEatableInBoardDelay = 0;
	DELETE_SAFELY_ARRAY(cells);
	touchUpPos->Set(0, 0);
	touchDownPos->Set(0, 0);
	posBackground->Set(0, 0);
	dropRowDelay = 0;
	rowDrop = 0;
	freeDiamonds.clear();
	eatBlockList.clear();
}

void Board::LoadMap(Level* level)
{
	width = level->GetWidth();
	height = level->GetHeight();
	
	cells = new Cell*[width*height];
	
	int* mapData = level->GetData();

	for(int i = 0; i<width*height; i++)
	{
		int xInBoard = i%width;
		int yInBoard = i/width;
		cells[i] = new Cell(mapData[i], xInBoard, yInBoard, this);	//Load lock info
		if(mapData[i] == 9) 
			cells[i]->SetIsAvailable(false);
	}
	RandomMissionItems(NUM_MISSION_ITEM_IN_MISSION_1);
}

void Board::SetState(int state)
{
	this->state = state;
	switch(state)
	{
	case BS_INIT_DIAMOND:
		RandomDiamond();
		dropRowDelay = 0;
		rowDrop = height;
		break;
	case BS_READY:
		freeDiamonds.clear();
		activeCell = 0;
		for(int i = 0; i < width*height; i++)
		{
			Diamond* diamond = cells[i]->GetDiamond();
			if(diamond != 0)
				diamond->SetIsEnableVibrate(true);
		}
		break;
	case BS_EATING:
		break;
	}
}

void Board::RandomDiamond()
{
	// init diamond
	for(int i = 0; i<width*height; i++)
	{
		int xNextBoard = i%width;
		int yNextBoard = i/width;
		Cell* cell = GetCell(xNextBoard, yNextBoard);
		if(cell->GetIsAvailable() == true)
		{
			Diamond* diamond = new Diamond(rand()%numTypeDiamond, this);
			diamond->pos->x = xNextBoard*cellHeight + pos->x;
			diamond->pos->y = yNextBoard*cellHeight + pos->y + INIT_DROP_POS;
			diamond->SetIsEnableVibrate(false);
			cell->SetDiamond(diamond);
			freeDiamonds.push_back(diamond);
		}
	}

	//check eatable in first time
	bool eatable = false;
	do 
	{
		eatable = false;
		for(int i = 0; i < width; i++)
		{
			for(int j = 0; j<height; j++)
			{
				while(CheckEatable(j, i)==true)
				{
					int id = GetCell(j, i)->GetDiamond()->GetID();
					if(id<numTypeDiamond-1)
						id++;
					else id=0;
					GetCell(j, i)->GetDiamond()->SetID(id);
					eatable = true;
				}
			}
		}
	}while(eatable == true);

	for(int i = 0; i<width; i++)
		for(int j = 0; j<height; j++)
	{
		GetCell(j, i)->SetDiamond(0);
	}
}

void Board::RandomDiamondWhenEat()
{
	list<EatBlockDiamond*>::iterator i;
	list<Diamond*>::iterator j;
	for(i = eatBlockList.begin(); i != eatBlockList.end(); i++)
	{
		EatBlockDiamond* eatBlock = (*i);
		if(eatBlock->IsAllInState(DMS_DIE) == false) continue;

		Diamond* mainDiamond = 0;

		int effect = eatBlock->GetEffectOfDiamondBornWhenEatBlockExplode();
		if(effect!= DME_NORMAL)
			mainDiamond = eatBlock->GetDiamondBornWhenEatBlockExplode();
		int maxYOfEatableDiamond = -10000;

		for(j = eatBlock->ateList.begin(); j != eatBlock->ateList.end(); j++)
		{
			Diamond* diamond = (*j);
			if(diamond->GetState()==DMS_DIE)
			{
				if(diamond->pos->y>maxYOfEatableDiamond)
					maxYOfEatableDiamond = diamond->pos->y;
			}
		}

		for(j = eatBlock->ateList.begin(); j != eatBlock->ateList.end(); j++)
		{
			Diamond* diamond = (*j);
			Diamond* newDiamond = new Diamond(rand()%numTypeDiamond, this);
			
			if(diamond == mainDiamond)		//create special diamond
			{
				newDiamond->pos->Set(mainDiamond->pos);
				newDiamond->SetID(mainDiamond->GetID());
				newDiamond->SetEffect(effect);
			}
			else{
				newDiamond->pos->Set(diamond->pos->x, pos->y - maxYOfEatableDiamond + diamond->pos->y + INIT_DROP_AFTER_ATE);
			}
			
			freeDiamonds.push_back(newDiamond);
		}
	}
	
}

static int freeDiamondSize = 0;
static int freeDiamondSizeOld = 0;
void Board::Update()
{
	float t = TimeController::GetInstance()->GetDtInSecond();
	list<EatBlockDiamond*>::iterator j;	
	list<Diamond*>::iterator i;	
	int numDiamondFilled = 0;
	
	freeDiamondSize = freeDiamonds.size();

	switch(state)
	{
	case BS_INIT_DIAMOND:
		{
		dropRowDelay += t;
		
		for(i = freeDiamonds.begin(); i!=freeDiamonds.end();i++)
		{
			(*i)->Update();
		}

		DropInDiamondInitState();

		//check first row, if all diamond in first row is filled, change to BS_READY state
		bool isAllDiamondReady = true;
		for(int i = 0; i < width; i++)
		{
			if(cells[i]->GetIsAvailable() == true)
			{
				Diamond* diamond = cells[i]->GetDiamond();
				if(diamond == 0)
				{
					isAllDiamondReady = false;
				}
			}
		}
		if(isAllDiamondReady == true)
			SetState(BS_READY);

		break;
		}
	case BS_READY:
		{
		// Check win/lose, if previous frame, has diamond droping down
		// and this frame has nothing.
		updateSomethingDelay += t;
		checkEatableInBoardDelay += t;

		if(updateSomethingDelay > TIME_DELAY_FOR_SOME_UPDATE)
		{
			if(CheckWin() == true)
				StateMgr::GetInstance()->SwitchState(new WinState());

			int a, b;
			if(IsNoWay(&a, &b) == true)
			{
				RandomDiamondId();
				PlayState::GetInstance()->gameTimer->DecreaseTime(10);
			}
			updateSomethingDelay = 0;
		}
		
		for(int i = 0; i < width; i++)
			for(int j = 0; j <height; j++)
				GetCell(j, i)->Update();

		//Process when special diamond ate.
		ProcessSpecialDiamondAte();

		//make ate diamond list explode after move
		for(j = eatBlockList.begin(); j!=eatBlockList.end(); j++)
			(*j)->KillDiamonds();

		// set diamond in freeDiamonds to
		UpdateFreeDiamonds();

		if(checkEatableInBoardDelay > TIME_DELAY_FOR_CHECK_EATABLE_IN_BOARD)
		{
			CheckEatableForDiamondInBoard();
			checkEatableInBoardDelay = 0;
		}
		

		//put free diamond to board if it idle
		RemoveDiamondFromFreeDiamondList();
		
		// after diamond die
		RandomDiamondWhenEat();
		SetDiamondMoveAfterEat();
		DestroyDiamondInDieState();

		//update mission items.
		MissionItem* mi;
		for(int iMission = 0; iMission < missionItems.size(); iMission++)
		{
			mi = missionItems[iMission];
			mi->Update();
		}

		break;
		}
	}

	freeDiamondSizeOld = freeDiamondSize;
}

void Board::UpdateFreeDiamonds()
{
	if(freeDiamondSize>0)
	{
		list<Diamond*>::iterator i;
		for(i = freeDiamonds.begin(); i!=freeDiamonds.end(); i++)
		{	
			Diamond* diamond = (*i);
			if(diamond!=0)
			{
				if(diamond->GetState()!=DMS_DROPDOWN_AUTO && diamond->GetState()!=DMS_DIE)
				{
					diamond->SetState(DMS_DROPDOWN_AUTO);
				}
				else {
					diamond->Update();
				}
			}
		}
	}
}

void Board::ProcessSpecialDiamondAte()
{
	list<EatBlockDiamond*>::iterator iEBD;
	list<Diamond*>::iterator iD;
	for(iEBD = eatBlockList.begin(); iEBD!= eatBlockList.end(); iEBD++)
	{
		EatBlockDiamond* eatBlock = (*iEBD);

		for(iD = eatBlock->ateList.begin(); iD != eatBlock->ateList.end(); iD++)
		{
			Diamond* diamond = (*iD);
			int effect = diamond->GetEffect();

			//if eat diamond has effect
			if(effect != DME_NORMAL)
			{
				Cell* mainCell = diamond->GetCell();
				if(mainCell == 0) continue;
				int x = mainCell->GetPosInBoard()->x;
				int y = mainCell->GetPosInBoard()->y;
				int left, right, top, bottom;			

				//set relative diamond will be eat
				switch(effect)
				{
				case DME_EAT4:
					left = x-1; right = x+1; top = y-1; bottom = y+1;
					if(left<0) left = 0;
					if(right > width - 1) right = width - 1;
					if(top <0) top = 0;
					if(bottom > height - 1) bottom = height - 1;
					for(int i = left; i<= right; i++)
						for(int j = top; j<=bottom; j++)
						{
							Cell* relativeCell = GetCell(i, j);
							if(relativeCell == 0) continue;
							Diamond* relativeDiamond = relativeCell->GetDiamond();
							if(relativeDiamond == 0) continue;

							eatBlock->AddDiamond(relativeDiamond);
						}
						break;
				case DME_EAT5:
					for(int i = 0; i< width*height; i++)
					{
						Cell* relativeCell = cells[i];
						Diamond* relativeDiamond = relativeCell->GetDiamond();
						if(relativeDiamond ==0) continue;
						if(relativeDiamond->GetID() == diamond->GetID())
							eatBlock->AddDiamond(relativeDiamond);
					}
				case DME_EAT5X5:
					//eat diamond in horizontal with special diamond
					for(int i = 0; i< width; i++)
					{
						Cell* relativeCell = GetCell(x, i);
						Diamond* relativeDiamond = relativeCell->GetDiamond();
						if(relativeDiamond ==0) continue;
						eatBlock->AddDiamond(relativeDiamond);
					}

					//eat diamond in vertical with special diamond
					for(int i = 0; i< height; i++)
					{
						Cell* relativeCell = GetCell(i, y);
						Diamond* relativeDiamond = relativeCell->GetDiamond();
						if(relativeDiamond ==0) continue;
						eatBlock->AddDiamond(relativeDiamond);
					}
					break;
				}
			}
		}

	}
}
void Board::DropInDiamondInitState()
{
	list<Diamond*>::iterator i;	
	//Effect drop
	if(rowDrop>=0)
	{
		if(dropRowDelay > TIME_TO_DROP_A_ROW)
		{
			rowDrop--;
			int beginIndex = rowDrop*width;
			int endIndex = (rowDrop+1)*width-1;
			int curIndex = 0;

			for(i = freeDiamonds.begin(); i!= freeDiamonds.end(); i++)
			{
				if(curIndex >= beginIndex)
				{
					(*i)->SetState(DMS_DROPDOWN_AUTO);
					if(curIndex==endIndex)
						break;
				}
				curIndex++;
			}
			dropRowDelay -= TIME_TO_DROP_A_ROW;
		}
	}
}

void Board::DestroyDiamondInDieState()
{
	list<EatBlockDiamond*>::iterator i;
	list<Diamond*>::iterator j;
	list<EatBlockDiamond*> removeList;
	for(i = eatBlockList.begin(); i != eatBlockList.end(); i++)
	{
		EatBlockDiamond* eatBlock = (*i);
		if(eatBlock->IsAllInState(DMS_DIE))
		{
			for(j = eatBlock->ateList.begin(); j != eatBlock->ateList.end(); j++)
			{
				Diamond* diamond = (*j);
				Cell* cell = diamond->GetCell();
				diamond->GetCell()->DecreaseLock();
				IsExistAndRemoveDiamondInFreeDiamondList(diamond);
				DELETE_SAFELY(diamond);
				cell->SetDiamond(0);
			}

			PlayState::GetInstance()->scoreBoard->AddScore(100);
			removeList.push_back(eatBlock);
			DELETE_SAFELY(eatBlock);
		}
	}

	for(i = removeList.begin(); i!=removeList.end(); i++)
	{
		EatBlockDiamond* eatBlock = (*i);
		eatBlockList.remove(eatBlock);
	}
}

bool Board::IsExistAndRemoveDiamondInFreeDiamondList(Diamond* diamond)
{
	list<Diamond*>::iterator i;
	list<Diamond*> removeList;
	bool isExist = false;
	for(i = freeDiamonds.begin(); i != freeDiamonds.end(); i++)
	{
		Diamond* curDiamond = (*i);
		if(curDiamond == diamond)
		{
			removeList.push_back(curDiamond);
			isExist = true;
			break;
		}
	}
	for(i = removeList.begin(); i!=removeList.end(); i++)
	{
		Diamond* needRemove= (*i);
		freeDiamonds.remove(needRemove);
	}
	return isExist;
}

void Board::Render()
{
	glClearColor(0, 0, 0, 1);
	// render background
	if(background != 0)
	{
		background->Render(posBackground->x, posBackground->y, 1.0f, 1.0f, 0.0f, 0.5, 0.0);
	}

	// render cells
	for(int i = 0; i<width*height; i++)
	{
		cells[i]->Render();
	}
	
	// render free diamonds
	list<Diamond*>::iterator i;
	for(i = freeDiamonds.begin(); i!=freeDiamonds.end();i++)
	{
		Diamond* diamond = (*i);
		if(diamond != 0)
			diamond->Render();
	}

	//render mission
	for(int i = 0; i < missionItems.size(); i++)
	{
		MissionItem* missionItem = missionItems[i];
		missionItem->Render(X_MISSION + W_SPRITE_DIAMOND*i, Y_MISSION);
	}
}

Board::~Board()
{
	DELETE_SAFELY_ARRAY(cells);
	DELETE_SAFELY(posBackground);
	DELETE_SAFELY(touchUpPos);
	DELETE_SAFELY(touchDownPos);
}

Cell* Board::GetCell(int x, int y)
{
	if(x>=0&&x<width&&y>=0&&y<height)
		return cells[y*width+x];
	else return 0;
}

void Board::SetBackground(Texture* texture)
{
	background = texture;
}

bool Board::CheckEatable(int x, int y)
{
	int sumEatable=1;
	bool eatable = false;

	Diamond* thisDiamond = GetCell(x, y)->GetDiamond();
	if(thisDiamond == 0) return false;
	int thisCellID = thisDiamond->GetID();
	
	//check left
	for(int i = x-1; i>=0; i--)
	{
		Cell* cell = GetCell(i, y);
		if(cell->GetDiamond() != 0)
		{
			if(cell->GetDiamond()->GetID()==thisCellID)
				sumEatable++;
			else break;
		}
		else break;
	}

	//check right
	for(int i = x+1; i<width; i++)
	{
		Cell* cell = GetCell(i, y);
		if(cell->GetDiamond() != 0)
		{
			if(cell->GetDiamond()->GetID()==thisCellID)
				sumEatable++;
			else break;
		}
		else break;
	}

	if(sumEatable > 2) eatable = true;

	sumEatable=1;

	//Check up
	for(int i = y-1; i>=0; i--)
	{
		Cell* cell = GetCell(x, i);
		if(cell->GetDiamond()!=0)
		{
			if(cell->GetDiamond()->GetID()==thisCellID)
				sumEatable++;
			else break;
		}
		else break;
	}

	//check down
	for(int i = y+1; i<height; i++)
	{
		Cell* cell = GetCell(x, i);
		if(cell->GetDiamond()!=0)
		{
			if(cell->GetDiamond()->GetID()==thisCellID)
				sumEatable++;
			else break;
		}
		else break;
	}

	if(sumEatable > 2) eatable = true;

	return eatable;
}

int Board::CheckEatableAndSet(Cell* cell)
{
	int x = cell->GetPosInBoard()->x;
	int y = cell->GetPosInBoard()->y;
	int sumEatableHorizontal=1;

	Diamond* thisDiamond = cell->GetDiamond();
	if(thisDiamond == 0) return -1;
	int thisCellID = thisDiamond->GetID();
	int numAddToEatableList=0;
	
	Diamond* diamond;
	EatBlockDiamond* eatBlock = new EatBlockDiamond();

	//check left
	for(int i = x-1; i>=0; i--)
	{
		diamond = GetCell(i, y)->GetDiamond();
		if(diamond != 0)
		{
			if(diamond->GetID()==thisCellID)
			{
				sumEatableHorizontal++;
				numAddToEatableList++;
				eatBlock->AddDiamond(diamond);
			}
			else break;
		}
		else break;
	}

	//check right
	for(int i = x+1; i<width; i++)
	{
		diamond = GetCell(i, y)->GetDiamond();
		if(diamond != 0)
		{
			if(diamond->GetID()==thisCellID)
			{
				sumEatableHorizontal++;
				numAddToEatableList++;
				eatBlock->AddDiamond(diamond);
			}
			else break;
		}
		else break;
	}
	
	if(numAddToEatableList==1)
		eatBlock->Pop();
	
	numAddToEatableList=0;
	int sumEatableVertical = 1;

	//Check up
	for(int i = y-1; i>=0; i--)
	{
		diamond = GetCell(x, i)->GetDiamond();
		if(diamond != 0)
		{
			if(diamond->GetID()==thisCellID)
			{
				sumEatableVertical++;
				numAddToEatableList++;
				eatBlock->AddDiamond(diamond);
			}
			else break;
		}
		else break;
	}

	//check down
	for(int i = y+1; i<height; i++)
	{
		diamond = GetCell(x, i)->GetDiamond();
		if(diamond != 0)
		{
			if(diamond->GetID()==thisCellID)
			{
				sumEatableVertical++;
				numAddToEatableList++;
				eatBlock->AddDiamond(diamond);
			}
			else break;
		}
		else break;
	}
	if(numAddToEatableList==1) 
		eatBlock->Pop();
	
	int size;
	//eat able
	if(sumEatableHorizontal>2||sumEatableVertical>2)
	{
		//add effect
		if(sumEatableHorizontal == 4 || sumEatableVertical == 4)
		{
			eatBlock->SetEffectOfDiamondBornWhenEatBlockExplode(DME_EAT4);
			eatBlock->SetDiamondBornWhenEatBlockExplode(thisDiamond);
		}
		if(sumEatableVertical == 5 && sumEatableVertical == 5)
		{
			eatBlock->SetEffectOfDiamondBornWhenEatBlockExplode(DME_EAT5X5);
			eatBlock->SetDiamondBornWhenEatBlockExplode(thisDiamond);
		}
		else if(sumEatableHorizontal == 5 || sumEatableVertical == 5)
		{
			eatBlock->SetEffectOfDiamondBornWhenEatBlockExplode(DME_EAT5);
			eatBlock->SetDiamondBornWhenEatBlockExplode(thisDiamond);
		}
		
		//add current diamond to eat block
		eatBlock->AddDiamond(thisDiamond);
		
		//Check mission match
        CheckMissionMatch(thisDiamond->GetID());
		
		if(eatBlock->Size()>0)
			eatBlockList.push_back(eatBlock);
		size = eatBlock->Size();
	}
	else
	{
		size = eatBlock->Size();
		DELETE_SAFELY(eatBlock);
	}
	return size;
}

int Board::CheckEatableAndSet(Cell* cell1, Cell* cell2)
{
	if(
		(cell1->GetPosInBoard()->x-cell2->GetPosInBoard()->x!=0||
		abs(cell1->GetPosInBoard()->y - cell2->GetPosInBoard()->y)>1)
		&&(cell1->GetPosInBoard()->y-cell2->GetPosInBoard()->y!=0||
		abs(cell1->GetPosInBoard()->x - cell2->GetPosInBoard()->x)>1))
			return -1;

	SwapDiamond(cell1, cell2);

	int resultCheckCell1 = CheckEatableAndSet(cell1);
	int resultCheckCell2 = CheckEatableAndSet(cell2);

	SwapDiamond(cell1, cell2);

	return resultCheckCell1 + resultCheckCell2;
}

int Board::CheckEatableFreeDiamond()
{
	list<Diamond*>::iterator i;
	int numDiamondEatable = 0;
	for(i = freeDiamonds.begin(); i != freeDiamonds.end(); i++)
	{
		Diamond* diamond = (*i);
		int eatable = CheckEatableAndSet(diamond->GetCell());
		numDiamondEatable += eatable;
	}
	return numDiamondEatable;
}

void Board::SwapDiamond(Cell* cell1, Cell* cell2)
{
	Diamond* diamond1 = cell1->GetDiamond();
	Diamond* diamond2 = cell2->GetDiamond();

	cell1->SetDiamond(diamond2);
	cell2->SetDiamond(diamond1);
}

bool Board::CheckMissionMatch(int id)
{
	int numActive = 0;						//number item in missionMatch is actived
	bool isMatch = false;
	bool isSetMissionItem = false;

	// set isActive to true if match
	for(int i = 0; i < missionItems.size(); i++)
	{
		MissionItem* missionItem = missionItems[i];
		if(missionItem->id == id && missionItem->GetIsActive() == false && isSetMissionItem == false)
		{
			missionItem->SetIsActive(true);
			isMatch = true;
			isSetMissionItem = true;
		}
		if(missionItem->GetIsActive() == true) 
			numActive++;
	}

	// reset if not match
	if(isMatch == false)
	{
		for(int i = 0; i < missionItems.size(); i++)
		{
			MissionItem* missionItem = missionItems[i];
			missionItem->SetIsActive(false);
		}
	}

	// check mission complete
	if(numActive == missionItems.size())		//mission complete
	{
		switch(missionItems.size())
		{
		case NUM_MISSION_ITEM_IN_MISSION_1:
			RandomMissionItems(NUM_MISSION_ITEM_IN_MISSION_2);
			numTypeDiamond++;
			break;
		case NUM_MISSION_ITEM_IN_MISSION_2:
			RandomMissionItems(NUM_MISSION_ITEM_IN_MISSION_3);
			numTypeDiamond++;
			break;
		}
		return true;
	}
	else return false;
}

void Board::TouchUp(int x, int y)
{
	touchUpPos->Set(x, y);

	if(state!=BS_INIT_DIAMOND)
	{
		int xBoard = (x-this->pos->x)/cellWidth;
		int yBoard = (y-this->pos->y)/cellHeight;

		if(xBoard<0||xBoard>=width||yBoard<0||yBoard>=height) return;

		Cell* cell;
		Diamond* diamond;

		//Check touch down and drag
		Vector subtractVector;
		touchUpPos->Subtract(touchDownPos, &subtractVector);
		if(subtractVector.Length() > 0.5*cellWidth)		// drag length enough, move diamond
		{
			int xBoardTouchDown = (touchDownPos->x-this->pos->x)/cellWidth;
			int yBoardTouchDown = (touchDownPos->y-this->pos->y)/cellHeight;

			// remove animation in old active cell...
			if(activeCell!=0)
				if(activeCell->GetDiamond()!=0)
					if(activeCell->GetDiamond()->GetState()==DMS_IDLE)
						activeCell->GetDiamond()->SetIsActive(false);
			// ... and set new active cell
			activeCell = GetCell(xBoardTouchDown, yBoardTouchDown);
			

			//direction
			int direction = -1;
			int absX = abs(subtractVector.x);
			int absY = abs(subtractVector.y);
			
			if(absX > absY)					// drag left or right
			{
				if(subtractVector.x > 0)	// right
					cell = GetCell(xBoardTouchDown+1, yBoardTouchDown);
				else cell = GetCell(xBoardTouchDown-1, yBoardTouchDown);
			}
			else							// drag top or bottom
			{
				if(subtractVector.y > 0)	// bottom
					cell = GetCell(xBoardTouchDown, yBoardTouchDown+1);
				else cell = GetCell(xBoardTouchDown, yBoardTouchDown-1);
			}

			// if current cell not available, set activeCell = 0,
			if(cell->GetIsAvailable() == false)
			{
				activeCell = 0;
				return;
			}

		}
		else
		{
			cell = GetCell(xBoard, yBoard);
		}
		if(cell == 0) return;
		diamond = cell->GetDiamond();

		if(diamond == 0) return;
		if(diamond->GetState() != DMS_IDLE || diamond->GetIsExploding()==true) return;

		if(activeCell==0)
		{
			diamond->SetIsActive(true);
			activeCell = cell;
		}
		else
		{
			if(cell == activeCell) return;
			Diamond* diamondActivedCell = activeCell->GetDiamond();
			if(diamondActivedCell == 0) return;
			if(diamondActivedCell->GetState() != DMS_IDLE || diamondActivedCell->GetIsExploding()==true){
				activeCell = 0;
				return;
			}
			int resultCheckEat = CheckEatableAndSet(cell, activeCell);

			if(resultCheckEat==-1)	//touch cross cell
			{
				if(diamondActivedCell!=0)
					diamondActivedCell->SetIsActive(false);
				diamond->SetIsActive(true);
				activeCell = cell;
			}
			else if(resultCheckEat>2)	//eat
			{
				if(diamond != 0 && diamondActivedCell != 0)
				{
					if(diamond->pos->x > diamondActivedCell->pos->x)
					{
						diamond->Move(DMD_LEFT, false);
						diamondActivedCell->Move(DMD_RIGHT,false);
					}
					else if(diamond->pos->x < diamondActivedCell->pos->x)
					{
						diamond->Move(DMD_RIGHT, false);
						diamondActivedCell->Move(DMD_LEFT, false);
					}
					else if(diamond->pos->y > diamondActivedCell->pos->y)
					{
						diamond->Move(DMD_TOP, false);
						diamondActivedCell->Move(DMD_BOTTOM, false);
					}
					else if(diamond->pos->y < diamondActivedCell->pos->y)
					{
						diamond->Move(DMD_BOTTOM, false);
						diamondActivedCell->Move(DMD_TOP, false);
					}
					SwapDiamond(cell, activeCell);
					activeCell = 0;			//reset active cell
				}
			}
			else					//can't eat
			{
				if(diamond->pos->x > diamondActivedCell->pos->x)
				{
					diamond->Move(DMD_LEFT, true);
					diamondActivedCell->Move(DMD_RIGHT,true);
				}
				else if(diamond->pos->x < diamondActivedCell->pos->x)
				{
					diamond->Move(DMD_RIGHT, true);
					diamondActivedCell->Move(DMD_LEFT, true);
				}
				else if(diamond->pos->y > diamondActivedCell->pos->y)
				{
					diamond->Move(DMD_TOP, true);
					diamondActivedCell->Move(DMD_BOTTOM, true);
				}
				else if(diamond->pos->y < diamondActivedCell->pos->y)
				{
					diamond->Move(DMD_BOTTOM, true);
					diamondActivedCell->Move(DMD_TOP, true);
				}
				activeCell = 0;			//reset active cell
			}
			
		}
	}
}

void Board::TouchDown(int x, int y)
{
	touchDownPos->Set(x, y);
}

void Board::TouchMove(int x, int y)
{

}

bool Board::IsAllDiamondInState(int state)
{
	for(int i = 0; i<width*height;i++)
	{
		if(cells[i]->GetIsAvailable() == false) continue;
		Diamond* diamond = cells[i]->GetDiamond();
		if(diamond==0) return false;
		if(diamond->GetState()!=state)
			return false;
	}
	return true;
}

int Board::NumDiamondInState(int state)
{
	int num = 0;
	for(int i = 0; i<width*height;i++)
	{
		Diamond* diamond = cells[i]->GetDiamond();
		if(diamond!=0)
		{
			if(diamond->GetState()==state)
				num++;
		}
	}
	return num;
}

void Board::SetDiamondMoveAfterEat()
{
	list<EatBlockDiamond*>::iterator i;
	list<Diamond*>::iterator j;
	list<EatBlockDiamond*> removeList;
	for(i = eatBlockList.begin(); i != eatBlockList.end(); i++)
	{
		EatBlockDiamond* eatBlock = (*i);
		if(eatBlock->IsAllInState(DMS_DIE) == true)
		{
			for(j = eatBlock->ateList.begin(); j != eatBlock->ateList.end(); j++)
			{
				Diamond* diamond = (*j);
				int curXBoard = diamond->GetCell()->GetPosInBoard()->x;
				int curYBoard = diamond->GetCell()->GetPosInBoard()->y;
				if(diamond->GetState()==DMS_DIE)
				{
					for(int i = curYBoard - 1; i >=0 ; i-- )
					{
						Diamond* aboveDiamonds = GetCell(curXBoard, i)->GetDiamond();
						if(aboveDiamonds!=0)
						{
							if(aboveDiamonds->GetState()==DMS_IDLE)
							{
								aboveDiamonds->GetCell()->SetDiamond(0);
								freeDiamonds.push_back(aboveDiamonds);
							}
							else if(aboveDiamonds->GetState()==DMS_MOVE)
							{
								aboveDiamonds->ResetPositionToItsCell();
								aboveDiamonds->GetCell()->SetDiamond(0);
								freeDiamonds.push_back(aboveDiamonds);
							}
						}
					}
				}
			}
		}
	}
}

void Board::CheckEatableForDiamondInBoard()
{
	//Check eatable for all diamond.
	for(int j = 0; j < height; j++)
		for(int k = 0; k < width; k++)
		{
			Cell* cell  = GetCell(k, j);
			Diamond* diamond = cell->GetDiamond();
			if(diamond!=0)										//if diamond available ...
			{
				if(IsExistDiamondInAteBlockList(diamond) == false)			// ... and not in ate list (prevent check more once time) ...
				{
					if(diamond->GetState() == DMS_IDLE 
						&& diamond->oldState != DMS_IDLE		
						&& diamond->GetIsExploding() == false)	// ... and isn't exploding ...
							CheckEatableAndSet(cell);			// ... let's check eatable or not.
				}
			}
		}
}

void Board::RemoveDiamondFromFreeDiamondList()
{
	list<Diamond*>::iterator i;
	list<Diamond*> freeDiamondRemove;

	for(i = freeDiamonds.begin(); i!=freeDiamonds.end(); i++)
	{
		Diamond* diamond = (*i);
		if(diamond->GetState()==DMS_IDLE)
		{
			freeDiamondRemove.push_back(diamond);
		}
	}

	for(i = freeDiamondRemove.begin(); i != freeDiamondRemove.end(); i++)
	{
		freeDiamonds.remove((*i));
	}
}

bool Board::CheckWin()
{
	// check normal or win
	for(int i = 0; i<width*height; i++)
	{
		if(cells[i]->GetLock()!=0)		//exist a cell with lock > 0, is not win
		{
			return false;					//RETURN normal
		}
	}
	return true;							// RETURN win
}

int Board::GetValue(int x, int y, int* boa)
{
	return boa[y*width + x];
}

bool Board::CheckEatableIntArray(int x, int y, int* boa)
{
	int sumEatable=1;

	int thisCellID = GetValue(x, y, boa);

	//check left
	for(int i = x-1; i>=0; i--)
	{
		int val = GetValue(i, y, boa);
		if(val==thisCellID && val != -1)
			sumEatable++;
		else break;
	}

	//check right
	for(int i = x+1; i<width; i++)
	{
		int val = GetValue(i, y, boa);
		if(val ==thisCellID && val != -1)
			sumEatable++;
		else break;
	}

	if(sumEatable > 2) return true;

	sumEatable=1;

	//Check up
	for(int i = y-1; i>=0; i--)
	{
		int val = GetValue(x, i, boa);
		if(val==thisCellID && val != -1)
			sumEatable++;
		else break;
	}

	//check down
	for(int i = y+1; i<height; i++)
	{
		int val = GetValue(x, i, boa);
		if(val==thisCellID && val != -1)
			sumEatable++;
		else break;
	}

	if(sumEatable > 2) return true;

	return false;
}

bool Board::IsNoWay(int* outX, int* outY)
{
	int* iBoard = new int[width*height];

	//set value for iBoard with ID of diamond in board.
	for(int i = 0; i < width*height; i++)
	{
		if(cells[i]->GetIsAvailable() == true)
		{
			Diamond* diamond = cells[i]->GetDiamond();
			if(diamond != 0) iBoard[i]= diamond->GetID();
			else iBoard[i] = -1;
		}
		else iBoard[i] = -1;
	}

	//swap and check
	for(int x = 0; x < width; x++)
	{
		for(int y = 0; y < height; y++)
		{
			if(iBoard[y*width + x] == -1) continue;
			//if right cell of cur cell is available, swap and check.
			if(x + 1 < width)
			{
				Swap(&iBoard[y*width + x], &iBoard[y*width + x + 1]);
				if(CheckEatableIntArray(x, y, iBoard) == true) 
				{
					*outX = x;
					*outY = y;
					return false;
				}

				if(CheckEatableIntArray(x + 1, y, iBoard) == true)
				{
					*outX = x + 1;
					*outY = y;
					return false;
				}
				Swap(&iBoard[y*width + x], &iBoard[y*width + x + 1]);
			}

			if(y + 1 < height)
			{
				Swap(&iBoard[y*width + x], &iBoard[(y+1)*width + x]);
				if(CheckEatableIntArray(x, y, iBoard) == true) 
				{
					*outX = x;
					*outY = y;
					return false;
				}

				if(CheckEatableIntArray(x, y + 1, iBoard) == true)
				{
					*outX = x;
					*outY = y + 1;
					return false;
				}
				Swap(&iBoard[y*width + x], &iBoard[(y+1)*width + x]);
			}
		}
	}
	return true;
}

void Board::Hint()
{
	int xHint = -1, yHint = -1;
	IsNoWay(&xHint, &yHint);
	if(xHint != -1 && yHint != -1)
	{
		if(activeCell != 0)
		{
			Diamond* diamond = activeCell->GetDiamond();
			if(diamond != 0)
				diamond->SetIsActive(false);
		}
			
		activeCell = GetCell(xHint, yHint);
		activeCell->GetDiamond()->SetIsActive(true);
	}
}

void Board::RandomDiamondId()
{
	int id = 0;
	for(int i = 0; i< width*height; i++)
	{
		Diamond* diamond = cells[i]->GetDiamond();
		if(diamond != 0)
		{
			id = rand()%numTypeDiamond;
			diamond->SetID(id);
		}
	}
}

void Board::RandomMissionItems(int numItem)
{
	// clear old mission item
	for(int i = 0; i < missionItems.size(); i++)
	{
		MissionItem* missionItem = missionItems[i];
		DELETE_SAFELY(missionItem);
	}
	missionItems.clear();

	//new mission item
	for(int i = 0; i < numItem; i++)
	{
		int id = rand()%numTypeDiamond;
		MissionItem* missionItem = new MissionItem(id);
		missionItems.push_back(missionItem);
	}
}

MissionItem::MissionItem(int id)
{
	this->id = id;
	isActive = false;
	isFlashing = false;
	isShow = true;
	flashDelay = 0;

	aniSprite = new AnimationSprite();
	Animation* ani = new Animation(0);
	ani->GenerateAuto(ResMgr::GetInstance()->GetTexture(TID_FRUIT),
		32, 32, 4, 0, 9, 0);
	aniSprite->AddAnimation(ani);
}

MissionItem::~MissionItem()
{
	DELETE_SAFELY(aniSprite);
}

void MissionItem::Render(int x, int y)
{
	float alpha = 0.3f;
	if(isActive == true)
		alpha = 1.0f;
	//show when item not flash or flashing but in flash on.
	if(isFlashing == false || (isFlashing == true && isShow == true))
	{
		aniSprite->GetCurAnimation()->SetSpriteIndex(true, id);
		aniSprite->Render(x, y, 1.0f, 1.0f, 0, alpha, 1);
	}
}

bool Board::IsExistDiamondInAteBlockList(Diamond* diamond)
{
	list<EatBlockDiamond*>::iterator i;
	EatBlockDiamond* eatBlock;
	for(i = eatBlockList.begin(); i != eatBlockList.end(); i++)
	{
		eatBlock = (*i);
		if(eatBlock->IsExistDiamond(diamond) == true)
			return true;
	}
	return false;
}

bool EatBlockDiamond::IsExistDiamond(Diamond* diamond)
{
	list<Diamond*>::iterator i;
	Diamond* diamondIte;
	for(i = ateList.begin(); i != ateList.end(); i++)
	{
		diamondIte = (*i);
		if(diamondIte == diamond)
			return true;
	}
	return false;
}

void MissionItem::SetIsActive(bool isActive)
{
	this->isActive = isActive;
	if(isActive == true)
	{
		flashDelay = 0;
		showDelay = 0;
		isFlashing = true;
		isShow = true;
	}
}

bool MissionItem::GetIsActive()
{
	return isActive;
}

void MissionItem::Update()
{
	float t = TimeController::GetInstance()->GetDtInSecond();
	if(isFlashing == true)
	{
		flashDelay += t;
		if(flashDelay > FLASH_TIME)
		{
			isFlashing = false;
			flashDelay = 0;
		}


		showDelay += t;
		if(showDelay > SHOW_TIME)
		{
			isShow = !isShow;
			showDelay = 0;
		}
		
	}
}